# -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp.osv import fields, osv
from openerp.tools.translate import _
import time
from openerp.tools import float_compare, DEFAULT_SERVER_DATETIME_FORMAT
from openerp import netsvc
import openerp.addons.decimal_precision as dp
from datetime import date, datetime
from openerp import SUPERUSER_ID
from openerp import api


class stock_return_picking(osv.osv_memory):
    _inherit = 'stock.return.picking'

    def default_get(self, cr, uid, fields, context=None):
        """
         To get default values for the object.
         @param self: The object pointer.
         @param cr: A database cursor
         @param uid: ID of the user currently logged in
         @param fields: List of fields for which we want default values
         @param context: A standard dictionary
         @return: A dictionary with default values for all field in ``fields``
        """
        result1 = []
        if context is None:
            context = {}
        res = super(stock_return_picking, self).default_get(cr, uid, fields, context=context)
        record_id = context and context.get('active_id', False) or False
        uom_obj = self.pool.get('product.uom')
        pick_obj = self.pool.get('stock.picking')
        pick = pick_obj.browse(cr, uid, record_id, context=context)
        quant_obj = self.pool.get("stock.quant")
        chained_move_exist = False
        if pick:
            if pick.state != 'done':
                raise osv.except_osv(_('Warning!'), _("You may only return pickings that are Done!"))

            for move in pick.move_lines:
                if move.move_dest_id:
                    chained_move_exist = True
                #Sum the quants in that location that can be returned (they should have been moved by the moves that were included in the returned picking)
                qty = 0
                quant_search = quant_obj.search(cr, uid, [('history_ids', 'in', move.id), ('qty', '>', 0.0), ('location_id', 'child_of', move.location_dest_id.id)], context=context)

                qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_uom_qty, move.product_id.uom_id.id)
                result1.append({'product_id': move.product_id.id, 'quantity': qty, 'move_id': move.id})

            if len(result1) == 0:
                raise osv.except_osv(_('Warning!'), _("No products to return (only lines in Done state and not fully returned yet can be returned)!"))
            if 'product_return_moves' in fields:
                res.update({'product_return_moves': result1})
            if 'move_dest_exists' in fields:
                res.update({'move_dest_exists': chained_move_exist})
        return res


    def _create_returns(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        context['allow_copy'] = True
        record_id = context and context.get('active_id', False) or False
        move_obj = self.pool.get('stock.move')
        pick_obj = self.pool.get('stock.picking')
        uom_obj = self.pool.get('product.uom')
        data_obj = self.pool.get('stock.return.picking.line')
        spo_obj = self.pool.get('stock.pack.operation')
        pick = pick_obj.browse(cr, uid, record_id, context=context)
        data = self.read(cr, uid, ids[0], context=context)
        returned_lines = 0

        # Cancel assignment of existing chained assigned moves
        moves_to_unreserve = []
        for move in pick.move_lines:
            to_check_moves = [move.move_dest_id]
            while to_check_moves:
                current_move = to_check_moves.pop()
                if current_move.state not in ('done', 'cancel') and current_move.reserved_quant_ids:
                    moves_to_unreserve.append(current_move.id)
                split_move_ids = move_obj.search(cr, uid, [('split_from', '=', current_move.id)], context=context)
                if split_move_ids:
                    to_check_moves += move_obj.browse(cr, uid, split_move_ids, context=context)

        if moves_to_unreserve:
            move_obj.do_unreserve(cr, uid, moves_to_unreserve, context=context)
            #break the link between moves in order to be able to fix them later if needed
            move_obj.write(cr, uid, moves_to_unreserve, {'move_orig_ids': False}, context=context)

        #Create new picking for returned products
        pick_type_id = pick.picking_type_id.return_picking_type_id and pick.picking_type_id.return_picking_type_id.id or pick.picking_type_id.id
        new_picking = pick_obj.copy(cr, uid, pick.id, {
            'allow_create': True,
            'move_lines': [],
            'picking_type_id': pick_type_id,
            'state': 'draft',
            'origin': pick.name,
            'ret_name': pick.name,
            'date_done': pick.date_done,
            'invoice_state': 'none',
        }, context=context)
#         raise osv.except_osv(_('Warning !'), _("applico"))
        for data_get in data_obj.browse(cr, uid, data['product_return_moves'], context=context):
            move = data_get.move_id
            if not move:
                raise osv.except_osv(_('Warning !'), _("You have manually created product lines, please delete them to proceed"))
            new_qty = data_get.quantity
            if new_qty:
                if new_qty < 0:
                    raise osv.except_osv(_('Warning !'), _("cannot input the negative qty"))
                qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_uom_qty, move.product_id.uom_id.id)

                if new_qty > qty:
                    raise osv.except_osv(_('Warning !'), _("the quantity returned cannot be more that the original qty (Qty Return: %s, Qty Original : %s)")%(new_qty, qty))
                returned_lines += 1
                move_obj.copy(cr, uid, move.id, {
                    'product_id': data_get.product_id.id,
                    'product_uom_qty': new_qty,
                    'product_uos_qty': uom_obj._compute_qty(cr, uid, move.product_uom.id, new_qty, move.product_uos.id),
                    'picking_id': new_picking,
                    'state': 'draft',
                    'location_id': move.location_dest_id.id,
                    'location_dest_id': move.location_id.id,
                    'origin_returned_move_id': move.id,
                    'procure_method': 'make_to_stock',
                    'restrict_lot_id': data_get.lot_id.id,
                    'price_unit': move.last_cost_price,
                    'purchase_line_id': False,
                    'last_product_qty': False,
                    'last_cost_price': False,
                    'move_id': False,
                })

        if not returned_lines:
            raise osv.except_osv(_('Warning!'), _("Please specify at least one non-zero quantity."))

        pick_obj.action_confirm(cr, uid, [new_picking], context=context)
        pick_obj.write(cr, uid, [new_picking], {'group_id': False, 'invoice_state': 'none'}, context=context)
        pick_obj.force_assign(cr, uid, [new_picking], context)

        for picking in pick_obj.browse(cr, uid, [new_picking], context=context):
            processed_ids = []
            packops2 = spo_obj.search(cr, uid, [('picking_id', '=', picking.id)])
            for packop2 in packops2:
                spo_obj.unlink(cr, uid, [packop2], context=context)

            if not picking.pack_operation_ids:
                picking.do_prepare_partial()
            for op in picking.pack_operation_ids:
                pack_datas = {
                    'product_id': op.product_id.id,
                    'product_uom_id': op.product_uom_id.id,
                    'product_qty': op.product_qty,
                    'package_id': op.package_id.id,
                    'lot_id': op.lot_id.id,
                    'location_id': op.location_id.id,
                    'location_dest_id': op.location_dest_id.id,
                    'result_package_id': op.result_package_id.id,
                    'date': picking.date_done or time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                    'owner_id': op.owner_id.id,
                }
                if op.id:
                    op.write(pack_datas)
                    processed_ids.append(op.id)

            # Delete the others
            packops = spo_obj.search(cr, uid, ['&', ('picking_id', '=', picking.id), '!', ('id', 'in', processed_ids)])
            for packop in packops:
                packop.unlink()


#                     picking_obj.do_partial(cr, uid, [picking_id], partial_data, context=context)
            pick_obj.do_transfer(cr, uid, [picking.id], context=context)

        return new_picking, pick_type_id

class account_invoice_line(osv.osv):
    _inherit = "account.invoice.line"

    _columns = {
        'stock_move_id': fields.many2one('stock.move',
            'Stock Move', ondelete='set null', select=True,
            readonly=True),
    }

account_invoice_line()

class stock_picking(osv.osv):

    _inherit = "stock.picking"

    def action_confirm_int(self, cr, uid, ids, context=None):
        todo = []
        todo_force_assign = []
        for picking in self.browse(cr, uid, ids, context=context):
            todo_force_assign.append(picking.id)
            for r in picking.move_lines:
                if r.state == 'draft':
                    todo.append(r.id)
        if len(todo):
            self.pool.get('stock.move').action_confirm(cr, uid, todo, context=context)

        if todo_force_assign:
            self.force_assign(cr, uid, todo_force_assign, context=context)
        return True

    def action_internal_done(self, cr, uid, ids, context=None):
        spo_obj = self.pool.get('stock.pack.operation')
        for picking in self.browse(cr, uid, ids, context=context):
            if picking.state in ('cancel'):
                continue
            processed_ids = []
            packops2 = spo_obj.search(cr, uid, [('picking_id', '=', picking.id)])
            for packop2 in packops2:
                spo_obj.unlink(cr, uid, [packop2], context=context)

            picking.do_prepare_partial()
            for op in picking.pack_operation_ids:
                pack_datas = {
                    'product_id': op.product_id.id,
                    'product_uom_id': op.product_uom_id.id,
                    'product_qty': op.product_qty,
                    'package_id': op.package_id.id,
                    'lot_id': op.lot_id.id,
                    'location_id': op.location_id.id,
                    'location_dest_id': op.location_dest_id.id,
                    'result_package_id': op.result_package_id.id,
                    'date': picking.date_done or time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                    'owner_id': op.owner_id.id,
                }
                if op.id:
                    op.write(pack_datas)
                    processed_ids.append(op.id)
    
            # Delete the others
            packops = spo_obj.search(cr, uid, ['&', ('picking_id', '=', picking.id), '!', ('id', 'in', processed_ids)])
            for packop in packops:
                packop.unlink()
    
            self.do_transfer_internal(cr, uid, [picking.id], context=context)

        return True

    def set_order_line_status(self, cr, uid, ids, status, context=None):
        line = self.pool.get('stock.move')
        order_line_ids = []
        for order in self.browse(cr, uid, ids, context=context):
            order_line_ids += [po_line.id for po_line in order.move_lines]
        if order_line_ids:
            line.write(cr, uid, order_line_ids, {'state': status}, context=context)
        return True

    def action_cancel_draft(self, cr, uid, ids, context=None):
        if not len(ids):
            return False
        self.write(cr, uid, ids, {'state':'draft'})
        self.set_order_line_status(cr, uid, ids, 'draft', context=context)
        for p_id in ids:
            # Deleting the existing instance of workflow for PO
            self.delete_workflow(cr, uid, [p_id]) # TODO is it necessary to interleave the calls?
            self.create_workflow(cr, uid, [p_id])
        return True

    def unlink(self, cr, uid, ids, context=None):
        move_obj = self.pool.get('stock.move')
        if context is None:
            context = {}
        for pick in self.browse(cr, uid, ids, context=context):
            if pick.state in ['assigned', 'done','cancel','draft']:
                raise osv.except_osv(_('Error!'), _('You cannot remove the Transaction which is in %s state!')%(pick.state,))
 
        return super(stock_picking, self).unlink(cr, uid, ids, context=context)
 
    def allow_cancel2(self, cr, uid, ids, context=None):
        for pick in self.browse(cr, uid, ids, context=context):
            if pick.invoice_state == 'invoiced':
                raise osv.except_osv(_('Error!'), _('You cannot cancel the Incoming Shipment until Invoice Control in Non Invoiced!'))
            if not pick.move_lines:
                return True
        return True

 
    def action_cancel2(self, cr, uid, ids, context=None):
        """ Changes picking state to cancel.
        @return: True
        """
        self.allow_cancel2(cr, uid, ids, context)
#         raise osv.except_osv(_('Error!'), _('You cannot duplicate the picking!'))
        for pick in self.browse(cr, uid, ids, context=context):
#             if pick.purchase_id:
#                 self.pool.get('purchase.order').write(cr, uid, pick.purchase_id.id, {'shipped': False})

            ids2 = [move.id for move in pick.move_lines]
            self.pool.get('stock.move').action_cancel(cr, uid, ids2, context)
        self.write(cr, uid, ids, {'state': 'cancel', 'invoice_state': 'none'})
        return True
 
    def revert_invoice(self, cr, uid, ids, context=None):
        account_invoice_obj = self.pool.get('account.invoice')
        for pick in self.browse(cr, uid, ids, context=context):
            for move in pick.move_lines:
                if move.invoice_line_ids:
                    for lines in move.invoice_line_ids:
                        if lines.invoice_id.state != 'cancel':
                            raise osv.except_osv(_('Error!'), _('cannot revert to non invoiced until delete or cancel the related invoice'))
            self.write(cr, uid, pick.id, {'invoice_state':'2binvoiced',}, context=context)
        return True
 
    def copy(self, cr, uid, id, default=None, context=None):
        if context is None:
            context={}
         
        if not default:
            default = {}
        allow_copy = ('allow_copy' in context and context['allow_copy']) or False
        if not allow_copy:
            raise osv.except_osv(_('Error!'), _('You cannot duplicate the Transaction!'))
 
        return super(stock_picking, self).copy(cr, uid, id, default=default,
                context=context)




    @api.cr_uid_ids_context
    def do_transfer_internal(self, cr, uid, picking_ids, context=None):
        """
            If no pack operation, we do simple action_done of the picking
            Otherwise, do the pack operations
        """
        if not context:
            context = {}
        ctx = context.copy()
        stock_move_obj = self.pool.get('stock.move')
        for picking in self.browse(cr, uid, picking_ids, context=context):
            date_qry = picking.date_done if picking.date_done else time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
            ctx.update({'date': date_qry})
            if not picking.pack_operation_ids:
                self.action_done(cr, uid, [picking.id], context=context)
                continue
            else:
                need_rereserve, all_op_processed = self.picking_recompute_remaining_quantities(cr, uid, picking, context=context)
                #create extra moves in the picking (unexpected product moves coming from pack operations)
                todo_move_ids = []
                if not all_op_processed:
                    todo_move_ids += self._create_extra_moves(cr, uid, picking, context=context)

                picking.refresh()
                #split move lines eventually

                toassign_move_ids = []
                for move in picking.move_lines:
                    remaining_qty = move.remaining_qty
                    if move.state in ('done', 'cancel'):
                        #ignore stock moves cancelled or already done
                        continue
                    elif move.state == 'draft':
                        toassign_move_ids.append(move.id)
                    if remaining_qty == 0:
                        if move.state in ('draft', 'assigned', 'confirmed'):
                            todo_move_ids.append(move.id)
                    elif remaining_qty > 0 and remaining_qty < move.product_qty:
                        new_move = stock_move_obj.split(cr, uid, move, remaining_qty, context=context)
                        todo_move_ids.append(move.id)
                        #Assign move as it was assigned before
                        toassign_move_ids.append(new_move)
                if need_rereserve or not all_op_processed: 
                    if not picking.location_id.usage in ("supplier", "production", "inventory"):
                        self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context)
                    self.do_recompute_remaining_quantities(cr, uid, [picking.id], context=context)
                if todo_move_ids and not context.get('do_only_split'):
                    self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=ctx)
                elif context.get('do_only_split'):
                    context = dict(context, split=todo_move_ids)
            picking.refresh()
            self._create_backorder(cr, uid, picking, context=context)
            if toassign_move_ids:
                stock_move_obj.force_assign(cr, uid, toassign_move_ids, context=context)

#         raise osv.except_osv(_('Error!'),  _('Done ...'))
        return True

    def update_price_unit(self, cr, uid, move, currency_id, context=None):
        currency_obj = self.pool.get('res.currency')
        stock_move_obj = self.pool.get('stock.move')
        uom_obj = self.pool.get('product.uom')
        if move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal':
            new_price = 0.00
            if move.purchase_line_id:
                product_price = move.purchase_line_id.price_unit
                new_price = currency_obj.compute(cr, uid, currency_id,
                        move.company_id.currency_id.id, product_price, round=False,context=context)
                new_price = uom_obj._compute_price(cr, uid, move.product_uom.id, new_price,
                        move.product_id.uom_id.id)
            else:
                new_price = move.price_unit
            stock_move_obj.write(cr, uid, [move.id],
                    {'price_unit': round(new_price,self.pool.get('decimal.precision').precision_get(cr, uid, 'Product Price')),
                     })
        return True

    @api.cr_uid_ids_context
    def do_transfer(self, cr, uid, picking_ids, context=None):
        """
            If no pack operation, we do simple action_done of the picking
            Otherwise, do the pack operations
        """
        if not context:
            context = {}
        ctx = context.copy()
        ctx['allow_copy'] = True
        stock_move_obj = self.pool.get('stock.move')
        currency_obj = self.pool.get('res.currency')
        uom_obj = self.pool.get('product.uom')
        product_obj = self.pool.get('product.product')
        for picking in self.browse(cr, uid, picking_ids, context=context):
            date_qry = picking.date_done if picking.date_done else time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
            ctx.update({'date': date_qry})

            product_avail, price_unit_avail = {}, {}

            if not picking.pack_operation_ids:
                self.action_done(cr, uid, [picking.id], context=context)
                continue
            else:
                need_rereserve, all_op_processed = self.picking_recompute_remaining_quantities(cr, uid, picking, context=context)
                #create extra moves in the picking (unexpected product moves coming from pack operations)
                todo_move_ids = []

                if not all_op_processed:
                    raise osv.except_osv(_('Error!'), _('The Qty entered is more than the qty can process!'))
                    todo_move_ids += self._create_extra_moves(cr, uid, picking, context=context)

                picking.refresh()
                #split move lines eventually

                toassign_move_ids = []
                for move in picking.move_lines:
                    company = move.company_id
                    partner, user_id, currency_id = stock_move_obj._get_master_data(cr, uid, move, company, context=context)
                    
                    remaining_qty = move.remaining_qty
                    if move.state in ('done', 'cancel'):
                        #ignore stock moves cancelled or already done
                        continue


                    elif move.state == 'draft':
                        toassign_move_ids.append(move.id)
                    if remaining_qty == 0:
                        if move.state in ('draft', 'assigned', 'confirmed'):
                            self.update_price_unit(cr, uid, move, currency_id, context=ctx)
                            
                            todo_move_ids.append(move.id)
                    elif remaining_qty > 0 and remaining_qty < move.product_qty:
                        new_move = stock_move_obj.split(cr, uid, move, remaining_qty, context=context)
                        self.update_price_unit(cr, uid, move, currency_id, context=ctx)
                        todo_move_ids.append(move.id)
                        #Assign move as it was assigned before
                        toassign_move_ids.append(new_move)
                if need_rereserve or not all_op_processed: 
                    if not picking.location_id.usage in ("supplier", "production", "inventory"):
                        self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context)
                    self.do_recompute_remaining_quantities(cr, uid, [picking.id], context=context)
#                 raise osv.except_osv(_('Error!'),  _('Cannot create. xxx'))
                if todo_move_ids and not context.get('do_only_split'):
                    self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=ctx)
                elif context.get('do_only_split'):
                    context = dict(context, split=todo_move_ids)
                for todo_move in stock_move_obj.browse(cr, uid, todo_move_ids, context=context):
                    company = todo_move.company_id
                    partner, user_id, currency_id = stock_move_obj._get_master_data(cr, uid, todo_move, company, context=context)
                    
                    type_pick = ''
                    if todo_move.location_id.usage == 'internal':
                        type_pick = 'out'
                        if todo_move.location_dest_id.usage == 'internal':
                            type_pick = 'internal'
                    else:
                        type_pick = 'in'
                    if (type_pick == 'in') and (todo_move.product_id.cost_method == 'average'):
                        product = todo_move.product_id
                        move_currency_id = todo_move.company_id.currency_id.id
#  
                        ctx['currency_id'] = move_currency_id
                        qty = todo_move.product_qty
                        if product.id not in product_avail:
                            # keep track of stock on hand including processed lines not yet marked as done
                            data_found = False
                            cr.execute('''select
                                sm.last_product_qty as last_product,
                                sm.last_cost_price as last_cost,
                                sm.date as sm_date,
                                sm.id as sm_id
                                from
                                    stock_move sm 
                                left join 
                                    stock_location sl on sm.location_id =sl.id
                                left join
                                    stock_location sld on sm.location_dest_id = sld.id
                                where
                                    sm.state = 'done' and sm.date <= %s and sm.company_id = %s and sl.usage <> sld.usage and sm.product_id = %s and sm.id != %s
                                order by 
                                    sm.date desc, sm.id desc''',(date_qry,todo_move.company_id.id,product.id, todo_move.id))
                            data_crs = cr.dictfetchall()
#                             print data_crs
                            ls_cost = 0.00
                            ls_pr = 0.00
                            if data_crs:
                                for data_cr in data_crs:
                                    sm_move_id = data_cr['sm_id']
                                    sm_date = data_cr['sm_date']
                                    if sm_date == date_qry:
                                        if sm_move_id > todo_move.id:
                                            continue
                                    if data_found:
                                        continue
                                    data_found = True
                                    ls_cost = (data_cr and 'last_cost' in data_cr and data_cr['last_cost']) or 0.00
                                    ls_pr = (data_cr and  'last_product' in data_cr and data_cr['last_product']) or 0.00
                            price_unit_avail[product.id] = ls_cost
                            product_avail[product.id] = ls_pr
#                             print price_unit_avail
#                             print product_avail
                        if qty > 0:


                            product_price = todo_move.purchase_line_id.price_unit

                            new_price = currency_obj.compute(cr, uid, currency_id,
                                    move_currency_id, product_price, round=False,context=ctx)
#     
                            new_price = uom_obj._compute_price(cr, uid, todo_move.product_uom.id, new_price,
                                    product.uom_id.id)

                            if product_avail[product.id] <= 0:
                                new_std_price = new_price
                            else:
                                # Get the standard price
                                amount_unit = price_unit_avail[product.id]
                                new_std_price = ((amount_unit * product_avail[product.id])\
                                    + (new_price * qty))/(product_avail[product.id] + qty)
                            new_std_price = round(new_std_price,self.pool.get('decimal.precision').precision_get(cr, uid, 'Product Price'))
     
                            # Write the field according to price type field
                             
                             
     
                            # Record the values that were chosen in the wizard, so they can be
                            # used for inventory valuation if real-time valuation is enabled.
                            stock_move_obj.write(cr, uid, [todo_move.id],
                                    {'price_unit': round(new_price,self.pool.get('decimal.precision').precision_get(cr, uid, 'Product Price')),
                                     'last_product_qty': product_avail[product.id] + qty,
                                     'last_cost_price': new_std_price
                                     })
     
                            product_avail[product.id] += qty

                            price_unit_avail[product.id] = new_std_price

                            last_cost = stock_move_obj._update_the_less_move(cr, uid, product, new_std_price, product_avail[product.id], date_qry, todo_move.company_id.id, move_currency_id, todo_move)
     
                            product_obj.write(cr, uid, [product.id], {'standard_price': last_cost})
                    if (type_pick == 'out') and (todo_move.product_id.cost_method == 'average'):
                        product = todo_move.product_id
                        move_currency_id = todo_move.company_id.currency_id.id
                        ctx['currency_id'] = move_currency_id
                        qty = todo_move.product_qty

                        #select previous qty and unit_cost
    #                     
                        if product.id not in product_avail:
                            # keep track of stock on hand including processed lines not yet marked as done
                            data_found = False
                            cr.execute('''select
                                sm.last_product_qty as last_product,
                                sm.last_cost_price as last_cost,
                                sm.date as sm_date,
                                sm.id as sm_id
                                from
                                    stock_move sm 
                                left join 
                                    stock_location sl on sm.location_id =sl.id
                                left join
                                    stock_location sld on sm.location_dest_id = sld.id
                                where
                                    sm.state = 'done' and sm.date <= %s and sm.company_id = %s and sl.usage <> sld.usage and sm.product_id = %s and sm.id != %s
                                order by 
                                    sm.date desc, sm.id desc''',(date_qry,todo_move.company_id.id,product.id,todo_move.id))
                            

                            data_crs = cr.dictfetchall()
                            ls_cost = 0.00
                            ls_pr = 0.00
                            if data_crs:
                                for data_cr in data_crs:
                                    sm_move_id = data_cr['sm_id']
                                    sm_date = data_cr['sm_date']
                                    if sm_date == date_qry:
                                        if sm_move_id > todo_move.id:
                                            continue
                                    if data_found:
                                        continue
                                    data_found = True
                                    ls_cost = (data_cr and 'last_cost' in data_cr and data_cr['last_cost']) or 0.00
                                    ls_pr = (data_cr and  'last_product' in data_cr and data_cr['last_product']) or 0.00
                            price_unit_avail[product.id] = ls_cost
                            product_avail[product.id] = ls_pr

                        if qty > 0:
                            product_avail[product.id] -= qty
                            if product_avail[product.id] < 0:
                                price_unit_avail[product.id] = 0.00
                            stock_move_obj.write(cr, uid, [todo_move.id],
                                    {'price_unit': price_unit_avail[product.id],
                                     'last_product_qty': product_avail[product.id],
                                     'last_cost_price': price_unit_avail[product.id]
                                     })
    
                            last_cost = stock_move_obj._update_the_less_move(cr, uid, product, price_unit_avail[product.id], product_avail[product.id], date_qry, todo_move.company_id.id, move_currency_id, todo_move)

                            product_obj.write(cr, uid, [product.id], {'standard_price': last_cost})

            picking.refresh()
            self._create_backorder(cr, uid, picking, context=ctx)
            if toassign_move_ids:
                stock_move_obj.force_assign(cr, uid, toassign_move_ids, context=context)
# 
        return True

    def _create_backorder(self, cr, uid, picking, backorder_moves=[], context=None):
        """ Move all non-done lines into a new backorder picking. If the key 'do_only_split' is given in the context, then move all lines not in context.get('split', []) instead of all non-done lines.
        """
        if not backorder_moves:
            backorder_moves = picking.move_lines
        sm_date = ('date' in context and context['date']) or time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)

        backorder_move_ids = [x.id for x in backorder_moves if x.state not in ('done', 'cancel')]
        if 'do_only_split' in context and context['do_only_split']:
            backorder_move_ids = [x.id for x in backorder_moves if x.id not in context.get('split', [])]

        if backorder_move_ids:
            backorder_id = self.copy(cr, uid, picking.id, {
                'name': '/',
                'move_lines': [],
                'pack_operation_ids': [],
                'backorder_id': picking.id,
            },context)
            backorder = self.browse(cr, uid, backorder_id, context=context)
            self.message_post(cr, uid, picking.id, body=_("Back order <em>%s</em> <b>created</b>.") % (backorder.name), context=context)
            move_obj = self.pool.get("stock.move")
            move_obj.write(cr, uid, backorder_move_ids, {'picking_id': backorder_id}, context=context)

            self.write(cr, uid, [picking.id], {'date_done': sm_date}, context=context)
            self.action_confirm(cr, uid, [backorder_id], context=context)
            return backorder_id
        return False

#     def _get_name_long(self, cr, uid, ids, field_name, args, context=None):
#         res = dict.fromkeys(ids, False)
#         for move in self.browse(cr, uid, ids, context=context):
#             if move.ret_name:
#                 res[move.id] = move.name + ' - Return Of - ' + move.ret_name
#             else:
#                 res[move.id] = move.name
#         return res

    _columns = {
#         'name_long': fields.function(_get_name_long, type='char', string='Reference'),
        'ret_name': fields.char('Return Reference', readonly=True, copy=False),
        'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)], 'assigned': [('readonly', True)]}),
        'allow_create': fields.boolean('Allow Draft Created'),
        'footer_note': fields.char('Footer Note'),
    }

    def create(self, cr, user, vals, context=None):
        context = context or {}
        allow_create = vals.get('allow_create', False)
        if not allow_create:
            raise osv.except_osv(_('Warning!'), _('not allowed creating this transaction.'))
        return super(stock_picking, self).create(cr, user, vals, context)

stock_picking()

# class stock_picking_in(osv.osv):
# 
#     _inherit = "stock.picking.in"
# 
#     def allow_cancel2(self, cr, uid, ids, context=None):
#         for pick in self.browse(cr, uid, ids, context=context):
#             if pick.invoice_state == 'invoiced':
#                 raise osv.except_osv(_('Error!'), _('You cannot cancel the Incoming Shipment until Invoice Control in Non Invoiced!'))
#             if not pick.move_lines:
#                 return True
#         return True
# 

# 
# 
# stock_picking_in()

class stock_move(osv.osv):

    _inherit = "stock.move"

    _columns = {
        'move_id': fields.many2one('account.move', 'Account Move'),
        'last_product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure')),
        'last_cost_price': fields.float('Unit Price', digits_compute= dp.get_precision('Product Price'), help="Technical field used to record the product cost set by the user during a picking confirmation (when average price costing method is used)"),
        'invoice_line_ids': fields.one2many('account.invoice.line', 'stock_move_id', 'Invoices Line', readonly=True),
        'stock_inventory_ids': fields.many2many('stock.inventory', 'stock_inventory_move_rel', 'move_id', 'inventory_id', 'Created Inventories'),
   }

#     def create(self, cr, uid, vals, context=None):
# #         raise osv.except_osv(_('Invalid Action !'), _('Applico.'))
#         return res_id


    def _get_price_unit_invoice(self, cr, uid, move_line, type, context=None):
        """ Gets price unit for invoice
        @param move_line: Stock move lines
        @param type: Type of invoice
        @return: The price unit for the move line
        """
#         raise osv.except_osv(_('Invalid Action !'), _('Applico.'))
        if context is None:
            context = {}
        if type in ('in_invoice', 'in_refund'):
            return move_line.price_unit
        else:
            # If partner given, search price in its sale pricelist
            if move_line.procurement_id.sale_line_id:
                price = move_line.procurement_id.sale_line_id.price_unit
#             if move_line.partner_id and move_line.partner_id.property_product_pricelist:
#                 pricelist_obj = self.pool.get("product.pricelist")
#                 pricelist = move_line.partner_id.property_product_pricelist.id
#                 price = pricelist_obj.price_get(cr, uid, [pricelist],
#                         move_line.product_id.id, move_line.product_uom_qty, move_line.partner_id.id, {
#                             'uom': move_line.product_uom.id,
#                             'date': move_line.date,
#                             })[pricelist]
#                 print price
                if price:
                    return price
            
#         raise osv.except_osv(_('Invalid Action !'), _('Applico.'))
        return move_line.product_id.list_price

    def _picking_assign(self, cr, uid, move_ids, procurement_group, location_from, location_to, context=None):
        """Assign a picking on the given move_ids, which is a list of move supposed to share the same procurement_group, location_from and location_to
        (and company). Those attributes are also given as parameters.
        """
        pick_obj = self.pool.get("stock.picking")
        picks = pick_obj.search(cr, uid, [
                ('group_id', '=', procurement_group),
                ('location_id', '=', location_from),
                ('location_dest_id', '=', location_to),
                ('state', 'in', ['draft', 'confirmed', 'waiting'])], context=context)
        if picks:
            pick = picks[0]
        else:
            move = self.browse(cr, uid, move_ids, context=context)[0]
            values = {
                'allow_create': True,
                'origin': move.origin,
                'company_id': move.company_id and move.company_id.id or False,
                'move_type': move.group_id and move.group_id.move_type or 'direct',
                'partner_id': move.partner_id.id or False,
                'picking_type_id': move.picking_type_id and move.picking_type_id.id or False,
                'recompute_pack_op': True,
                
            }
            pick = pick_obj.create(cr, uid, values, context=context)
        self.write(cr, uid, move_ids, {'picking_id': pick}, context=context)
        return self.force_assign(cr, uid, move_ids, context=context)

    def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None):
        fp_obj = self.pool.get('account.fiscal.position')
        # Get account_id
        if inv_type in ('out_invoice', 'out_refund'):
            account_id = move.product_id.property_account_income.id
            if not account_id:
                account_id = move.product_id.categ_id.property_account_income_categ.id
        else:
            account_id = move.product_id.property_account_expense.id
            if not account_id:
                account_id = move.product_id.categ_id.property_account_expense_categ.id
        fiscal_position = partner.property_account_position
        account_id = fp_obj.map_account(cr, uid, fiscal_position, account_id)

        # set UoS if it's a sale and the picking doesn't have one
        uos_id = move.product_uom.id
        quantity = move.product_uom_qty
        if move.product_uos:
            uos_id = move.product_uos.id
            quantity = move.product_uos_qty
        return {
            'stock_move_id': move.id,
            'name': move.name,
            'account_id': account_id,
            'product_id': move.product_id.id,
            'uos_id': uos_id,
            'quantity': quantity,
            'price_unit': self._get_price_unit_invoice(cr, uid, move, inv_type),
            'discount': 0.0,
            'account_analytic_id': False,
        }

    def _prepare_account_move_line(self, cr, uid, move, qty, cost, credit_account_id, debit_account_id, context=None):
        """
        Generate the account.move.line values to post to track the stock valuation difference due to the
        processing of the given quant.
        """
        if context is None:
            context = {}
        currency_obj = self.pool.get('res.currency')
        if context.get('force_valuation_amount'):
            valuation_amount = context.get('force_valuation_amount')
        else:
            if move.product_id.cost_method == 'average':
                valuation_amount = move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal' and cost or move.product_id.standard_price
            else:
                valuation_amount = move.product_id.cost_method == 'real' and cost or move.product_id.standard_price
        #the standard_price of the product may be in another decimal precision, or not compatible with the coinage of
        #the company currency... so we need to use round() before creating the accounting entries.
        valuation_amount = currency_obj.round(cr, uid, move.company_id.currency_id, valuation_amount * qty)
#         raise osv.except_osv(_('Invalid Action !'), _('Please input Sequence Code before process.'))
        if move.location_dest_id.usage != 'internal' and move.product_id.cost_method == 'average':
            data_found = False
            cr.execute('''select sm.last_product_qty as last_qty,
                sm.last_cost_price as last_cost,
                sm.id as sm_id,
                sm.date as sm_date
                from
                    stock_move sm 
                left join 
                    stock_location sl on sm.location_id =sl.id
                left join
                    stock_location sld on sm.location_dest_id = sld.id
                where
                    sm.state = 'done' and sm.date <= %s and sm.company_id = %s and sl.usage <> sld.usage and sm.product_id = %s and sm.id != %s
                order by 
                    sm.date desc, sm.id desc''',(move.date,move.company_id.id,move.product_id.id, move.id))
            data_crs = cr.dictfetchall()
            ls_cost = 0.00
            ls_pr = 0.00
            if data_crs:
                for data_cr in data_crs:
                    sm_move_id = data_cr['sm_id']
                    sm_date = data_cr['sm_date']
                    if sm_date == move.date:
                        if sm_move_id > move.id:
                            continue
                    if data_found:
                        continue
                    data_found = True
                    ls_cost = (data_cr and 'last_cost' in data_cr and data_cr['last_cost']) or 0.00
                    ls_pr = (data_cr and  'last_qty' in data_cr and data_cr['last_qty']) or 0.00
            data_cr = cr.dictfetchone()
            last_qty = ls_pr
            last_cost = ls_cost
            if last_qty <= 0:
                last_cost = 0.00
            valuation_amount = qty * last_cost
            valuation_amount = round(valuation_amount,self.pool.get('decimal.precision').precision_get(cr, uid, 'Account'))

        partner_id = (move.picking_id.partner_id and self.pool.get('res.partner')._find_accounting_partner(move.picking_id.partner_id).id) or False
        debit_line_vals = {
                    'name': move.name,
                    'product_id': move.product_id.id,
                    'quantity': qty,
                    'product_uom_id': move.product_id.uom_id.id,
                    'ref': (move.picking_id and move.picking_id.name) or (move.inventory_id and move.inventory_id.name) or False,
                    'date': move.date,
                    'partner_id': partner_id,
                    'debit': valuation_amount > 0 and valuation_amount or 0,
                    'credit': valuation_amount < 0 and -valuation_amount or 0,
                    'account_id': debit_account_id,
        }
        credit_line_vals = {
                    'name': move.name,
                    'product_id': move.product_id.id,
                    'quantity': qty,
                    'product_uom_id': move.product_id.uom_id.id,
                    'ref':(move.picking_id and move.picking_id.name) or (move.inventory_id and move.inventory_id.name) or False,
                    'date': move.date,
                    'partner_id': partner_id,
                    'credit': valuation_amount > 0 and valuation_amount or 0,
                    'debit': valuation_amount < 0 and -valuation_amount or 0,
                    'account_id': credit_account_id,
        }
#         print (move.picking_id and move.picking_id.name) or (move.inventory_id and move.inventory_id.name)
#         print debit_line_vals
#         print credit_line_vals
#         raise osv.except_osv(_('Operation Forbidden!'),
#             _('Applico'))
        return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)]

    def _create_account_move_line(self, cr, uid, move, credit_account_id, debit_account_id, journal_id, context=None):

        move_obj = self.pool.get('account.move')
        stock_move_obj = self.pool.get('stock.move')
        stock_quant_obj = self.pool.get('stock.quant')
        cost = self.get_price_unit(cr, uid, move, context=context)
        
        move_lines = self._prepare_account_move_line(cr, uid, move, move.product_qty, cost, credit_account_id, debit_account_id, context=context)
        period_id = context.get('force_period', self.pool.get('account.period').find(cr, uid, move.date, context=context)[0])
        move_id = move_obj.create(cr, uid, {'journal_id': journal_id,
                                  'line_id': move_lines,
                                  'period_id': period_id,
                                  'date': move.date,
                                  'ref': (move.picking_id and move.picking_id.name) or (move.inventory_id and move.inventory_id.name) or ''}, context=context)

        stock_move_obj.write(cr, uid, [move.id], {'move_id': move_id})

    def _account_entry_move(self, cr, uid, move, context=None):

        if context is None:
            context = {}
        stock_quant_obj = self.pool.get('stock.quant')
        location_obj = self.pool.get('stock.location')
        location_from = move.location_id
        location_to = move.location_dest_id
        company_from = location_obj._location_owner(cr, uid, location_from, context=context)
        company_to = location_obj._location_owner(cr, uid, location_to, context=context)

        if move.product_id.valuation != 'real_time':
            return False

        if company_to and (move.location_id.usage not in ('internal', 'transit') and move.location_dest_id.usage == 'internal' or company_from != company_to):
            ctx = context.copy()
            ctx['force_company'] = company_to.id
            journal_id, acc_src, acc_dest, acc_valuation = stock_quant_obj._get_accounting_data_for_valuation(cr, uid, move, context=ctx)

            if not move.picking_id:
                inv_acc = move.inventory_id.int_type_id and move.inventory_id.int_type_id.property_adjustment_type_account and  move.inventory_id.int_type_id.property_adjustment_type_account.id or False
                if not inv_acc:
                    raise osv.except_osv(_('Error!'),  _('There is no inventory adjustment account defined for this type: "%s" (id: %s)') % \
                    (move.inventory_id.int_type_id.name, move.inventory_id.name,))
                acc_src = inv_acc
                acc_dest = inv_acc
            if location_from and location_from.usage == 'customer':
                #goods returned from customer
                self._create_account_move_line(cr, uid, move, acc_dest, acc_valuation, journal_id, context=ctx)
            else:
                self._create_account_move_line(cr, uid, move, acc_src, acc_valuation, journal_id, context=ctx)

        # Create Journal Entry for products leaving the company
        if company_from and (move.location_id.usage == 'internal' and move.location_dest_id.usage not in ('internal', 'transit') or company_from != company_to):
            ctx = context.copy()
            ctx['force_company'] = company_from.id
            journal_id, acc_src, acc_dest, acc_valuation = stock_quant_obj._get_accounting_data_for_valuation(cr, uid, move, context=ctx)
            if not move.picking_id:
                inv_acc = move.inventory_id.int_type_id and move.inventory_id.int_type_id.property_adjustment_type_account and  move.inventory_id.int_type_id.property_adjustment_type_account.id or False
                if not inv_acc:
                    raise osv.except_osv(_('Error!'),  _('There is no inventory adjustment account defined for this type: "%s" (id: %s)') % \
                    (move.inventory_id.int_type_id.name, move.inventory_id.name,))
                acc_src = inv_acc
                acc_dest = inv_acc

            if location_to and location_to.usage == 'supplier':
                #goods returned to supplier
                self._create_account_move_line(cr, uid, move, acc_valuation, acc_src, journal_id, context=ctx)
            else:
                self._create_account_move_line(cr, uid,  move, acc_valuation, acc_dest, journal_id, context=ctx)


    def _update_the_less_move(self, cr, uid, product, last_cost, last_qty, date_start, company_id, move_currency_id, move, context=None):

#         raise osv.except_osv(_('Operation Forbidden!'),
#             _('Applico'))
        uom_obj = self.pool.get('product.uom')
        currency_obj = self.pool.get('res.currency')
        stock_move_obj = self.pool.get('stock.move')
        move_obj = self.pool.get('account.move')
        stock_quant_obj = self.pool.get('stock.quant')
        
        if context is None:
            context = {}
        cr.execute('''select
            sm.id as sm_move_id,
            sm.product_qty as sm_product_qty,
            sm.price_unit as sm_price_unit,
            sm.product_uom as sm_product_uom,
            sm.date as sm_date,
            sl.usage as sl_usage,
            sm.move_id as move_id
            from
                stock_move sm 
            left join 
                stock_location sl on sm.location_id =sl.id
            left join
                stock_location sld on sm.location_dest_id = sld.id
            where
                sm.state = 'done' and sm.date >= %s and sm.company_id = %s and sl.usage <> sld.usage and sm.product_id = %s and sm.id != %s
            order by 
                sm.date, sm.id ''',(date_start, company_id, product.id, move.id))

        data_crs = cr.dictfetchall()
        if data_crs:
            for data_cr in data_crs:
                sm_move_id = data_cr['sm_move_id']
                price_unit = data_cr['sm_price_unit'] or 0.00
                product_uom = data_cr['sm_product_uom']
                product_qty = data_cr['sm_product_qty'] or 0.00
                stock_move = stock_move_obj.browse(cr, uid, sm_move_id, context=None)
                partner, user_id, product_currency = stock_move_obj._get_master_data(cr, uid, stock_move, stock_move.company_id, context=context)
                sm_date = data_cr['sm_date']
                if sm_date == date_start:
                    if sm_move_id < move.id:
                        continue
                move_id = data_cr['move_id'] or False
                qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
                if data_cr['sl_usage'] == 'internal':
                    last_qty -= qty

                    stock_move_obj.write(cr, uid, [sm_move_id],
                            {'last_product_qty': last_qty,
                             'last_cost_price': last_cost,
                             'price_unit': last_cost,
                             })
                    context['date'] = sm_date
                    if move_id:

                        move_obj.button_cancel(cr, uid, [move_id], context=context)
                        move_obj.unlink(cr, uid, [move_id], context=context)
                    if stock_move.product_id.valuation == 'real_time':
                        self._account_entry_move(cr, uid, stock_move, context)
                else:
                    context['date'] = sm_date
                    new_price = currency_obj.compute(cr, uid, product_currency,
                            move_currency_id, price_unit, round=False,context=context)
                    new_price = uom_obj._compute_price(cr, uid, product_uom, new_price,
                            product.uom_id.id)

                    if last_qty <= 0:
                        new_std_price = new_price
                    else:
                        # Get the standard price
                        amount_unit = last_cost
                        new_std_price = ((amount_unit * last_qty)\
                            + (new_price * qty))/(last_qty + qty)
                    last_qty += qty

                    prec = self.pool.get('decimal.precision').precision_get(cr, uid, 'Product Price')
                    last_cost = round(new_std_price, prec)


                    stock_move_obj.write(cr, uid, [sm_move_id],
                            {'last_product_qty': last_qty,
                             'last_cost_price': last_cost,
                             })
        return last_cost

    def _quant_create(self, cr, uid, move, qty, location_id, context=None):
        '''Create a quant in the destination location and create a negative quant in the source location if it's an internal location.
        '''

        if context is None:
            context = {}
        sm_date =  datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
        price_unit = self.pool.get('stock.move').get_price_unit(cr, uid, move, context=context)
        stock_quant_obj = self.pool.get('stock.quant')

        vals = {
            'product_id': move.product_id.id,
            'location_id': location_id,
            'qty': qty,
            'cost': price_unit,
            'history_ids': [(4, move.id)],
            'in_date': sm_date,
            'company_id': move.company_id.id,
            'lot_id': False,
            'owner_id': False,
            'package_id': False,
        }

        #create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
        quant_id = stock_quant_obj.create(cr, SUPERUSER_ID, vals, context=context)
        return True


    def action_cancel(self, cr, uid, ids, context=None):
        """ Cancels the moves and if all moves are cancelled it cancels the picking.
        @return: True
        """
        status = ''
        for move in self.browse(cr, uid, ids, context=context):
            status = move.state
            
        procurement_obj = self.pool.get('procurement.order')
#         <----->
        stock_quant_obj = self.pool.get('stock.quant')
        move_obj = self.pool.get("account.move")
        product_obj = self.pool.get("product.product")
        context = context or {}
#         <----->
        procs_to_check = []
        res = self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
        for move in self.browse(cr, uid, ids, context=context):

#             if move.state == 'done':
#                 raise osv.except_osv(_('Operation Forbidden!'),
#                         _('You cannot cancel a stock move that has been set to \'Done\'.'))
            if move.reserved_quant_ids:
                self.pool.get("stock.quant").quants_unreserve(cr, uid, move, context=context)
            if context.get('cancel_procurement'):

                if move.propagate:

                    procurement_ids = procurement_obj.search(cr, uid, [('move_dest_id', '=', move.id)], context=context)
                    procurement_obj.cancel(cr, uid, procurement_ids, context=context)
            else:
                if move.move_dest_id:
                    if move.propagate:
                        self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)



                    elif move.move_dest_id.state == 'waiting':
                        #If waiting, the chain will be broken and we are not sure if we can still wait for it (=> could take from stock instead)
                        self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
                if move.procurement_id:
                    # Does the same as procurement check, only eliminating a refresh
                    procs_to_check.append(move.procurement_id.id)
                    
#         <----->
            if move.move_id:
                move_obj.button_cancel(cr, uid, [move.move_id.id], context=context)
                move_obj.unlink(cr, uid, [move.move_id.id], context=context)
            if move.location_id.usage != move.location_dest_id.usage:
                if status != 'done':
                    continue
                move_currency_id = move.company_id.currency_id.id
                cr.execute('''select
                    sm.last_product_qty as last_product,
                    sm.last_cost_price as last_cost
                    from
                        stock_move sm 
                    left join 
                        stock_location sl on sm.location_id =sl.id
                    left join
                        stock_location sld on sm.location_dest_id = sld.id
                    where
                        sm.state = 'done' and sm.date <= %s and sm.company_id = %s and sm.product_id = %s and sl.usage <> sld.usage and sm.id <> %s
                    order by 
                        sm.date desc, sm.id desc limit 1''',(move.date,move.company_id.id,move.product_id.id, move.id))
                
                data_cr = cr.dictfetchone()
                price_unit = (data_cr and 'last_cost' in data_cr and data_cr['last_cost']) or 0.00
                product_qty = (data_cr and 'last_product' in data_cr and data_cr['last_product']) or 0.00

                last_cost = self._update_the_less_move(cr, uid, move.product_id, price_unit, product_qty, move.date, move.company_id.id, move_currency_id, move)
                product_obj.write(cr, SUPERUSER_ID, [move.product_id.id], {'standard_price': last_cost})
            if move.location_dest_id.usage == 'internal' and move.location_id.usage == 'internal':
                if status != 'done':
                    continue
                self._quant_create(cr, uid, move, move.product_qty * -1, move.location_dest_id.id, context=context)
                self._quant_create(cr, uid, move, move.product_qty, move.location_id.id, context=context)
            if move.location_dest_id.usage == 'internal' and move.location_id.usage != 'internal':
                if status != 'done':
                    continue
                self._quant_create(cr, uid, move, move.product_qty * -1, move.location_dest_id.id, context=context)
            if move.location_id.usage == 'internal' and move.location_dest_id.usage != 'internal':
                if status != 'done':
                    continue
                self._quant_create(cr, uid, move, move.product_qty, move.location_id.id, context=context)
                
#                 quant_ids = []
#                 for quant in move.quant_ids:
# 
#                     cr.execute('''select
#                         sqmr.move_id as move_id
#                         from
#                             stock_quant_move_rel sqmr
#                         left join 
#                             stock_move sm on sqmr.move_id = sm.id
#                         left join
#                             stock_location sl on sm.location_id = sl.id
#                         left join
#                             stock_location sld on sm.location_dest_id = sld.id
#                         where
#                             sqmr.quant_id = %s and sqmr.move_id <> %s and sld.id = '%s'
#                         order by sm.id limit 1''',(quant.id,move.id,move.location_id.id))
#                     data_cr = cr.dictfetchone()
# 
#                     if data_cr:
#                         mv_quant_id = ('move_id' in data_cr and data_cr['move_id']) or False
#                         cr.execute('''select
#                             count(sqmr.*) as numb
#                             from
#                                 stock_quant_move_rel sqmr
#                             left join 
#                                 stock_quant sq on sqmr.quant_id = sq.id
#                             left join 
#                                 stock_move sm on sqmr.move_id = sm.id
#                             left join 
#                                 stock_location sl on sm.location_id = sl.id
#                             left join
#                                 stock_location sld on sm.location_dest_id = sld.id
#                             where
#                                 sqmr.move_id = %s and sq.location_id = %s and sqmr.quant_id not in %s''',(mv_quant_id,move.location_id.id,(quant_ids and tuple(quant_ids)) or tuple([0,])))
#                         
#                         data_cr = cr.dictfetchone()
#                         count_mv_quant_id = ('numb' in data_cr and data_cr['numb']) or 0
#                         if count_mv_quant_id > 1:
#                             cr.execute('''select
#                                 sqmr.quant_id as qua
#                                 from
#                                     stock_quant_move_rel sqmr
#                                 left join 
#                                     stock_quant sq on sqmr.quant_id = sq.id
#                                 left join 
#                                     stock_move sm on sqmr.move_id = sm.id
#                                 left join 
#                                     stock_location sl on sm.location_id = sl.id
#                                 left join
#                                     stock_location sld on sm.location_dest_id = sld.id
#                                 where
#                                     sqmr.move_id = %s and sqmr.quant_id <> %s AND sq.location_id = %s
#                                  order by sq.in_date desc limit 1''',(mv_quant_id,quant.id, move.location_id.id))
#                             data_cr = cr.dictfetchone()
# 
# #                             raise osv.except_osv(_('Operation Forbidden!'),
# #                                                 _('Applico'))
#                             qua = ('qua' in data_cr and data_cr['qua']) or False
#                             if qua:
#                                 qua_id = stock_quant_obj.browse(cr, uid, qua)
#                                 stock_quant_obj.write(cr, uid, [qua_id.id], {'qty': (qua_id.qty + quant.qty)})
#                                 stock_quant_obj.unlink(cr, uid, [quant.id], context=context)
#                         else:
# 
#                             stock_quant_obj.write(cr, uid, [quant.id], {'location_id': (move.location_id.id)})
#                             cr.execute('''delete
#                                 from
#                                     stock_quant_move_rel
#                                 where
#                                     move_id = %s and quant_id = %s  ''',(move.id,quant.id))
# #                             self.write(cr, uid, [move.id], {'quant_ids': []})
#                     else:
# 
#                         stock_quant_obj.unlink(cr, uid, [quant.id], context=context)
#                     quant_ids.append(quant.id)
#         <----->
        
        if procs_to_check:
            procurement_obj.check(cr, uid, procs_to_check, context=context)
#         raise osv.except_osv(_('Operation Forbidden!'),
#                  _('Applico'))
        return res


    def action_done(self, cr, uid, ids, context=None):
        """ Process completely the moves given as ids and if all moves are done, it will finish the picking.
        """
        context = context or {}
        picking_obj = self.pool.get("stock.picking")
        quant_obj = self.pool.get("stock.quant")
        
        sm_date = ('date' in context and context['date']) or time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
        self.write(cr, uid, ids, {'date': sm_date}, context=context)

        todo = [move.id for move in self.browse(cr, uid, ids, context=context) if move.state == "draft"]
        if todo:
            ids = self.action_confirm(cr, uid, todo, context=context)
        pickings = set()
        procurement_ids = []
        #Search operations that are linked to the moves
        operations = set()
        move_qty = {}
        for move in self.browse(cr, uid, ids, context=context):
            move_qty[move.id] = move.product_qty
            for link in move.linked_move_operation_ids:
                operations.add(link.operation_id)

        #Sort operations according to entire packages first, then package + lot, package only, lot only
        operations = list(operations)
        operations.sort(key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0))

        for ops in operations:
            if ops.picking_id:
                pickings.add(ops.picking_id.id)
            main_domain = [('qty', '>', 0)]
            for record in ops.linked_move_operation_ids:
                move = record.move_id
                self.check_tracking(cr, uid, move, not ops.product_id and ops.package_id.id or ops.lot_id.id, context=context)
                prefered_domain = [('reservation_id', '=', move.id)]
                fallback_domain = [('reservation_id', '=', False)]
                fallback_domain2 = ['&', ('reservation_id', '!=', move.id), ('reservation_id', '!=', False)]
                prefered_domain_list = [prefered_domain] + [fallback_domain] + [fallback_domain2]
                dom = main_domain + self.pool.get('stock.move.operation.link').get_specific_domain(cr, uid, record, context=context)
                quants = quant_obj.quants_get_prefered_domain(cr, uid, ops.location_id, move.product_id, record.qty, domain=dom, prefered_domain_list=prefered_domain_list,
                                                          restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
                if ops.result_package_id.id:
                    #if a result package is given, all quants go there
                    quant_dest_package_id = ops.result_package_id.id
                elif ops.product_id and ops.package_id:
                    #if a package and a product is given, we will remove quants from the pack.
                    quant_dest_package_id = False
                else:
                    #otherwise we keep the current pack of the quant, which may mean None
                    quant_dest_package_id = ops.package_id.id
                quant_obj.quants_move(cr, uid, quants, move, ops.location_dest_id, location_from=ops.location_id, lot_id=ops.lot_id.id, owner_id=ops.owner_id.id, src_package_id=ops.package_id.id, dest_package_id=quant_dest_package_id, context=context)
                # Handle pack in pack
                if not ops.product_id and ops.package_id and ops.result_package_id.id != ops.package_id.parent_id.id:
                    self.pool.get('stock.quant.package').write(cr, SUPERUSER_ID, [ops.package_id.id], {'parent_id': ops.result_package_id.id}, context=context)
                move_qty[move.id] -= record.qty
        #Check for remaining qtys and unreserve/check move_dest_id in
        move_dest_ids = set()
        for move in self.browse(cr, uid, ids, context=context):
            if move_qty[move.id] > 0:  # (=In case no pack operations in picking)
                main_domain = [('qty', '>', 0)]
                prefered_domain = [('reservation_id', '=', move.id)]
                fallback_domain = [('reservation_id', '=', False)]
                fallback_domain2 = ['&', ('reservation_id', '!=', move.id), ('reservation_id', '!=', False)]
                prefered_domain_list = [prefered_domain] + [fallback_domain] + [fallback_domain2]
                self.check_tracking(cr, uid, move, move.restrict_lot_id.id, context=context)
                qty = move_qty[move.id]
                quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, qty, domain=main_domain, prefered_domain_list=prefered_domain_list, restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
                quant_obj.quants_move(cr, uid, quants, move, move.location_dest_id, lot_id=move.restrict_lot_id.id, owner_id=move.restrict_partner_id.id, context=context)

            # If the move has a destination, add it to the list to reserve
            if move.move_dest_id and move.move_dest_id.state in ('waiting', 'confirmed'):
                move_dest_ids.add(move.move_dest_id.id)

            if move.procurement_id:
                procurement_ids.append(move.procurement_id.id)

            #unreserve the quants and make them available for other operations/moves
            quant_obj.quants_unreserve(cr, uid, move, context=context)


        # Check the packages have been placed in the correct locations
        self._check_package_from_moves(cr, uid, ids, context=context)
        #set the move as done

        self.write(cr, uid, ids, {'state': 'done'}, context=context)
        self.pool.get('procurement.order').check(cr, uid, procurement_ids, context=context)
        #assign destination moves
        if move_dest_ids:
            self.action_assign(cr, uid, list(move_dest_ids), context=context)
        #check picking state to set the date_done is needed
        done_picking = []
        for picking in picking_obj.browse(cr, uid, list(pickings), context=context):
            if picking.state == 'done':
                done_picking.append(picking.id)

        if done_picking:
            picking_obj.write(cr, uid, done_picking, {'date_done': sm_date}, context=context)

        return True

    def _get_master_data(self, cr, uid, move, company, context=None):
        if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.order_policy == 'picking':
            sale_order = move.procurement_id.sale_line_id.order_id
            return sale_order.partner_id, sale_order.user_id.id, sale_order.pricelist_id.currency_id.id
        return super(stock_move, self)._get_master_data(cr, uid, move, company, context=context)


stock_move()

class stock_quant(osv.osv):

    _inherit = "stock.quant"


    def _price_update(self, cr, uid, quant_ids, newprice, context=None):
        if context is None:
            context = {}
        self.write(cr, SUPERUSER_ID, quant_ids, {'cost': newprice}, context=context)
        ctx = context.copy()
        move_obj = self.pool.get('account.move')
        for quant in self.browse(cr, uid, quant_ids, context=context):
            move = self._get_latest_move(cr, uid, quant, context=context)
            # this is where we post accounting entries for adjustment
            ctx['force_valuation_amount'] = newprice - quant.cost
            
            if move.move_id:
                move_obj.button_cancel(cr, uid, [move.move_id.id], context=context)
                move_obj.unlink(cr, uid, [move.move_id.id], context=context)

            self._account_entry_move(cr, uid, [quant], move, context=ctx)
            #update the standard price of the product, only if we would have done it if we'd have had enough stock at first, which means
            #1) the product cost's method is 'real'
            #2) we just fixed a negative quant caused by an outgoing shipment
            if quant.product_id.cost_method == 'real' and quant.location_id.usage != 'internal':
                self.pool.get('stock.move')._store_average_cost_price(cr, uid, move, context=context)

    def _quant_create(self, cr, uid, qty, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False,
                      force_location_from=False, force_location_to=False, context=None):
        '''Create a quant in the destination location and create a negative quant in the source location if it's an internal location.
        '''

        if context is None:
            context = {}
        sm_date = ('date' in context and context['date']) or datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
        price_unit = self.pool.get('stock.move').get_price_unit(cr, uid, move, context=context)
        location = force_location_to or move.location_dest_id

        vals = {
            'product_id': move.product_id.id,
            'location_id': location.id,
            'qty': qty,
            'cost': price_unit,
            'history_ids': [(4, move.id)],
            'in_date': sm_date,
            'company_id': move.company_id.id,
            'lot_id': lot_id,
            'owner_id': owner_id,
            'package_id': dest_package_id,
        }

        if move.location_id.usage == 'internal':
            #if we were trying to move something from an internal location and reach here (quant creation),
            #it means that a negative quant has to be created as well.
            negative_vals = vals.copy()
            negative_vals['location_id'] = force_location_from and force_location_from.id or move.location_id.id
            negative_vals['qty'] = -qty
            negative_vals['cost'] = price_unit
            negative_vals['negative_move_id'] = move.id
            negative_vals['package_id'] = src_package_id
            negative_quant_id = self.create(cr, SUPERUSER_ID, negative_vals, context=context)
            vals.update({'propagated_from_id': negative_quant_id})

        #create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
        quant_id = self.create(cr, SUPERUSER_ID, vals, context=context)
        quant = self.browse(cr, uid, quant_id, context=context)
        if move.product_id.valuation == 'real_time':
            self._account_entry_move(cr, uid, [quant], move, context)
        return quant

    def _account_entry_move(self, cr, uid, quants, move, context=None):
        """
        Accounting Valuation Entries

        quants: browse record list of Quants to create accounting valuation entries for. Unempty and all quants are supposed to have the same location id (thay already moved in)
        move: Move to use. browse record
        """
        if context is None:
            context = {}
        location_obj = self.pool.get('stock.location')
        location_from = move.location_id
        location_to = quants[0].location_id
        company_from = location_obj._location_owner(cr, uid, location_from, context=context)
        company_to = location_obj._location_owner(cr, uid, location_to, context=context)

        if move.product_id.valuation != 'real_time':
            return False
        for q in quants:
            if q.owner_id:
                #if the quant isn't owned by the company, we don't make any valuation entry
                return False
            if q.qty <= 0:
                #we don't make any stock valuation for negative quants because the valuation is already made for the counterpart.
                #At that time the valuation will be made at the product cost price and afterward there will be new accounting entries
                #to make the adjustments when we know the real cost price.
                return False

        #in case of routes making the link between several warehouse of the same company, the transit location belongs to this company, so we don't need to create accounting entries
        # Create Journal Entry for products arriving in the company
        if company_to and (move.location_id.usage not in ('internal', 'transit') and move.location_dest_id.usage == 'internal' or company_from != company_to):
            ctx = context.copy()
            ctx['force_company'] = company_to.id
            journal_id, acc_src, acc_dest, acc_valuation = self._get_accounting_data_for_valuation(cr, uid, move, context=ctx)

            if not move.picking_id:
                inv_acc = move.inventory_id.int_type_id and move.inventory_id.int_type_id.property_adjustment_type_account and  move.inventory_id.int_type_id.property_adjustment_type_account.id or False
                if not inv_acc:
                    raise osv.except_osv(_('Error!'),  _('There is no inventory adjustment account defined for this type: "%s" (id: %s)') % \
                    (move.inventory_id.int_type_id.name, move.inventory_id.name,))
                acc_src = inv_acc
                acc_dest = inv_acc
            if location_from and location_from.usage == 'customer':
                #goods returned from customer
                self._create_account_move_line(cr, uid, quants, move, acc_dest, acc_valuation, journal_id, context=ctx)
            else:
                self._create_account_move_line(cr, uid, quants, move, acc_src, acc_valuation, journal_id, context=ctx)

        # Create Journal Entry for products leaving the company
        if company_from and (move.location_id.usage == 'internal' and move.location_dest_id.usage not in ('internal', 'transit') or company_from != company_to):
            ctx = context.copy()
            ctx['force_company'] = company_from.id
            journal_id, acc_src, acc_dest, acc_valuation = self._get_accounting_data_for_valuation(cr, uid, move, context=ctx)
            if not move.picking_id:
                inv_acc = move.inventory_id.int_type_id and move.inventory_id.int_type_id.property_adjustment_type_account and  move.inventory_id.int_type_id.property_adjustment_type_account.id or False
                if not inv_acc:
                    raise osv.except_osv(_('Error!'),  _('There is no inventory adjustment account defined for this type: "%s" (id: %s)') % \
                    (move.inventory_id.int_type_id.name, move.inventory_id.name,))
                acc_src = inv_acc
                acc_dest = inv_acc

            if location_to and location_to.usage == 'supplier':
                #goods returned to supplier
                self._create_account_move_line(cr, uid, quants, move, acc_valuation, acc_src, journal_id, context=ctx)
            else:
                self._create_account_move_line(cr, uid, quants, move, acc_valuation, acc_dest, journal_id, context=ctx)

    def _prepare_account_move_line(self, cr, uid, move, qty, cost, credit_account_id, debit_account_id, context=None):
        """
        Generate the account.move.line values to post to track the stock valuation difference due to the
        processing of the given quant.
        """
        if context is None:
            context = {}
        currency_obj = self.pool.get('res.currency')
        if context.get('force_valuation_amount'):
            valuation_amount = context.get('force_valuation_amount')
        else:
            if move.product_id.cost_method == 'average':
                valuation_amount = move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal' and cost or move.product_id.standard_price
            else:
                valuation_amount = move.product_id.cost_method == 'real' and cost or move.product_id.standard_price
        #the standard_price of the product may be in another decimal precision, or not compatible with the coinage of
        #the company currency... so we need to use round() before creating the accounting entries.
        valuation_amount = currency_obj.round(cr, uid, move.company_id.currency_id, valuation_amount * qty)
#         raise osv.except_osv(_('Invalid Action !'), _('Please input Sequence Code before process.'))
        if move.location_dest_id.usage != 'internal' and move.product_id.cost_method == 'average':
            data_found = False
            cr.execute('''select sm.last_product_qty as last_qty,
                sm.last_cost_price as last_cost,
                sm.id as sm_id,
                sm.date as sm_date
                from
                    stock_move sm 
                left join 
                    stock_location sl on sm.location_id =sl.id
                left join
                    stock_location sld on sm.location_dest_id = sld.id
                where
                    sm.state = 'done' and sm.date <= %s and sm.company_id = %s and sl.usage <> sld.usage and sm.product_id = %s and sm.id != %s
                order by 
                    sm.date desc, sm.id desc''',(move.date,move.company_id.id,move.product_id.id, move.id))
            data_crs = cr.dictfetchall()
            ls_cost = 0.00
            ls_pr = 0.00
            if data_crs:
                for data_cr in data_crs:
                    sm_move_id = data_cr['sm_id']
                    sm_date = data_cr['sm_date']
                    if sm_date == move.date:
                        if sm_move_id > move.id:
                            continue
                    if data_found:
                        continue
                    data_found = True
                    ls_cost = (data_cr and 'last_cost' in data_cr and data_cr['last_cost']) or 0.00
                    ls_pr = (data_cr and  'last_qty' in data_cr and data_cr['last_qty']) or 0.00
            last_qty = ls_pr
            last_cost = ls_cost
            if last_qty <= 0:
                last_cost = 0.00
            valuation_amount = qty * last_cost
            valuation_amount = round(valuation_amount,self.pool.get('decimal.precision').precision_get(cr, uid, 'Account'))


        partner_id = (move.picking_id.partner_id and self.pool.get('res.partner')._find_accounting_partner(move.picking_id.partner_id).id) or False
        debit_line_vals = {
                    'name': move.name,
                    'product_id': move.product_id.id,
                    'quantity': qty,
                    'product_uom_id': move.product_id.uom_id.id,
                    'ref': move.picking_id and move.picking_id.name or False,
                    'date': move.date,
                    'partner_id': partner_id,
                    'debit': valuation_amount > 0 and valuation_amount or 0,
                    'credit': valuation_amount < 0 and -valuation_amount or 0,
                    'account_id': debit_account_id,
        }
        credit_line_vals = {
                    'name': move.name,
                    'product_id': move.product_id.id,
                    'quantity': qty,
                    'product_uom_id': move.product_id.uom_id.id,
                    'ref': move.picking_id and move.picking_id.name or False,
                    'date': move.date,
                    'partner_id': partner_id,
                    'credit': valuation_amount > 0 and valuation_amount or 0,
                    'debit': valuation_amount < 0 and -valuation_amount or 0,
                    'account_id': credit_account_id,
        }
#         print debit_line_vals
#         print credit_line_vals
#         raise osv.except_osv(_('Operation Forbidden!'),
#             _('Applico'))
        return [(0, 0, debit_line_vals), (0, 0, credit_line_vals)]


    def _create_account_move_line(self, cr, uid, quants, move, credit_account_id, debit_account_id, journal_id, context=None):
        #group quants by cost
        quant_cost_qty = {}

        for quant in quants:
            if quant_cost_qty.get(quant.cost):
                quant_cost_qty[quant.cost] += quant.qty
            else:
                quant_cost_qty[quant.cost] = quant.qty
        move_obj = self.pool.get('account.move')
        stock_move_obj = self.pool.get('stock.move')
        for cost, qty in quant_cost_qty.items():
            move_lines = self._prepare_account_move_line(cr, uid, move, qty, stock_move_obj.get_price_unit(cr, uid, move, context=context), credit_account_id, debit_account_id, context=context)
            period_id = context.get('force_period', self.pool.get('account.period').find(cr, uid, move.date, context=context)[0])
            move_id = move_obj.create(cr, uid, {'journal_id': journal_id,
                                      'line_id': move_lines,
                                      'period_id': period_id,
                                      'date': move.date,
                                      'ref': (move.picking_id and move.picking_id.name) or (move.inventory_id and move.inventory_id.name)or ''}, context=context)

            stock_move_obj.write(cr, uid, [move.id], {'move_id': move_id})


stock_quant()


class int_type(osv.osv):
    _name = "int.type"
    _description = "Inventory Adjustments Type"
 
    def create_sequence(self, cr, uid, sname, company_id, context=None):
        suffix = sname.upper()
 
        seq = {
            'name': "PI/" + suffix,
            'implementation':'no_gap',
            'prefix': "%(y)s",
            'suffix': suffix,
            'padding': 5,
            'number_increment': 1
        }
        if company_id:
            seq['company_id'] = company_id
        return self.pool.get('ir.sequence').create(cr, uid, seq)
 
    def btn_create_type_s(self, cr, uid, ids, context=None):
        for o in self.browse(cr, uid, ids):
            if not o.seq_name:
                raise osv.except_osv(_('Invalid Action !'), _('Please input Sequence Code before process.'))
            if not o.sequence_id:
                self.write(cr, uid, ids, {'sequence_id': self.create_sequence(cr, uid, o.seq_name, o.company_id.id, context)})
        return True
# 
    _columns = {
        'name': fields.char('Name', size=64, required=True),
        'desc': fields.char('Description', size=64, select=True),
        'seq_name': fields.char('Sequence Code', size=64, select=True),
        'sequence_id': fields.many2one('ir.sequence', 'Sequence', help="This field contains the information related to the numbering of the purchase order entries."),
        'company_id':fields.many2one('res.company', 'Company'),
        'type': fields.selection([('addiction', 'Addition'),
                                   ('reduction', 'Deduction')], 'Type',
                                   required=True),
        'property_adjustment_type_account': fields.property(
            type='many2one',
            relation='account.account',
            string='Account'),
    }
# 
    _defaults = {
        'company_id': lambda self, cr, uid, context: \
                self.pool.get('res.users').browse(cr, uid, uid,
                    context=context).company_id.id,
    }
int_type()

class stock_inventory(osv.osv):

    _inherit = "stock.inventory"

    _columns = {
        'int_type_id': fields.many2one('int.type', 'Sequence Type', required=True, states={'done': [('readonly', True)]}),
        'reason': fields.char('Reason', size=64),
        'move_ids': fields.one2many('stock.move', 'inventory_id', 'Created Moves', help="Inventory Moves.", readonly=True),
        'approved_zero': fields.boolean('approved zero'),
        'zero_approver':fields.many2one('res.users', 'Zero Allowed Approved By'),
        'zero_date': fields.datetime('Zero Allowed Approved Date'),
    }

    def action_cancel_inventory(self, cr, uid, ids, context=None):
        """ Cancels both stock move and inventory
        @return: True
        """
        move_obj = self.pool.get('stock.move')
        account_move_obj = self.pool.get('account.move')
        for inv in self.browse(cr, uid, ids, context=context):
            move_obj.action_cancel(cr, uid, [x.id for x in inv.move_ids], context=context)
            for move in inv.move_ids:
                 if move.move_id:
                     account_move_obj.unlink(cr, uid, [move.move_id.id], context=context)
            self.write(cr, uid, [inv.id], {'state': 'cancel'}, context=context)
        return True

    def unlink(self, cr, uid, id, default=None, context=None):
        if not default:
            default = {}
        raise osv.except_osv(_('Error!'), _('cannot delete Inventory Adjustment, except cancel it.'))
        return super(stock_inventory, self).unlink(cr, uid, id, default, context)


    def copy(self, cr, uid, id, default=None, context=None):
        if not default:
            default = {}
        raise osv.except_osv(_('Error!'), _('cannot duplicate Inventory Adjustment'))
        return super(stock_inventory, self).copy(cr, uid, id, default, context)


    def create(self, cr, user, vals, context=None):
        if 'int_type_id' in vals:
            obj_int_type = self.pool.get('int.type')
            obj_sequence = self.pool.get('ir.sequence')
            int_type = obj_int_type.browse(cr, user, vals['int_type_id'], context=None)
            seq_id = (int_type.sequence_id and int_type.sequence_id.id) or False
            if not seq_id:
                raise osv.except_osv(_('Invalid action !'), _('no default sequence found in "' + str(int_type.name) + '" Physical Inventories Type.'))

            new_name = obj_sequence.next_by_id(cr, user, seq_id, None)
            vals.update({'name' : new_name,
                     })
        new_id = super(stock_inventory, self).create(cr, user, vals, context)
        return new_id


    def action_done2(self, cr, uid, ids, context=None):
        """ Finish the inventory
        @return: True
        """
        uom_obj =  self.pool.get('product.uom')
        stock_move_obj = self.pool.get('stock.move')
        product_obj = self.pool.get('product.product')
        for inv in self.browse(cr, uid, ids, context=context):
            type = inv.int_type_id.type
            price_unit_avail, product_avail = {}, {}
            for inventory_line in inv.line_ids:
                if inventory_line.product_qty < 0:
                    raise osv.except_osv(_('Warning'), _('You cannot set a negative product quantity in an inventory line:\n\t%s - qty: %s' % (inventory_line.product_id.name, inventory_line.product_qty)))
            self.action_check(cr, uid, [inv.id], context=context)
            inv.refresh()
            for sm in inv.move_ids:
                qty = uom_obj._compute_qty(cr, uid, sm.product_uom.id, sm.product_uom_qty, sm.product_id.uom_id.id)
                if (type == 'addiction') and (sm.product_id.cost_method == 'average'):
                    move_currency_id = sm.company_id.currency_id.id
                    if sm.product_id.id not in product_avail:
                        # keep track of stock on hand including processed lines not yet marked as done
                        data_found = False
                        cr.execute('''select
                            sm.last_product_qty as last_product,
                            sm.last_cost_price as last_cost,
                            sm.date as sm_date,
                            sm.id as sm_id
                            from
                                stock_move sm 
                            left join 
                                stock_location sl on sm.location_id =sl.id
                            left join
                                stock_location sld on sm.location_dest_id = sld.id
                            where
                                sm.state = 'done' and sm.date <= %s and sm.company_id = %s and sl.usage <> sld.usage and sm.product_id = %s and sm.id != %s
                            order by 
                                sm.date desc, sm.id desc''',(inv.date,sm.company_id.id,sm.product_id.id, sm.id))
                        data_crs = cr.dictfetchall()
                        ls_cost = 0.00
                        ls_pr = 0.00
                        if data_crs:
                            for data_cr in data_crs:
                                sm_move_id = data_cr['sm_id']
                                sm_date = data_cr['sm_date']
                                if sm_date == inv.date:
                                    if sm_move_id > sm.id:
                                        continue
                                if data_found:
                                    continue
                                data_found = True
                                ls_cost = (data_cr and 'last_cost' in data_cr and data_cr['last_cost']) or 0.00
                                ls_pr = (data_cr and  'last_product' in data_cr and data_cr['last_product']) or 0.00
                        price_unit_avail[sm.product_id.id] = ls_cost
                        product_avail[sm.product_id.id] = ls_pr

                    if qty > 0:
                        new_price = sm.price_unit
                        if product_avail[sm.product_id.id] <= 0:
                            new_std_price = new_price
                        else:
                            # Get the standard price
                            amount_unit = price_unit_avail[sm.product_id.id]
                            new_std_price = ((amount_unit * product_avail[sm.product_id.id])\
                                + (new_price * qty))/(product_avail[sm.product_id.id] + qty)
                        new_std_price = round(new_std_price,self.pool.get('decimal.precision').precision_get(cr, uid, 'Product Price'))
                        
                        # Write the field according to price type field
                         
                         
                        
                        # Record the values that were chosen in the wizard, so they can be
                        # used for inventory valuation if real-time valuation is enabled.
                        stock_move_obj.write(cr, uid, [sm.id],
                                {'price_unit': new_price,
                                 'last_product_qty': product_avail[sm.product_id.id] + qty,
                                 'last_cost_price': new_std_price
                                 })
                        
                        product_avail[sm.product_id.id] += qty
                        price_unit_avail[sm.product_id.id] = new_std_price
                        ##################
                        last_cost = stock_move_obj._update_the_less_move(cr, uid, sm.product_id, new_std_price, product_avail[sm.product_id.id], inv.date, sm.company_id.id, move_currency_id, sm)
                        
                        product_obj.write(cr, uid, [sm.product_id.id], {'standard_price': last_cost})
                if (type == 'reduction') and (sm.product_id.cost_method == 'average'):
                        product = sm.product_id
                        move_currency_id = sm.company_id.currency_id.id
                        if product.id not in product_avail:
                            # keep track of stock on hand including processed lines not yet marked as done

                            data_found = False
                            cr.execute('''select
                                sm.last_product_qty as last_product,
                                sm.last_cost_price as last_cost,
                                sm.date as sm_date,
                                sm.id as sm_id
                                from
                                    stock_move sm 
                                left join 
                                    stock_location sl on sm.location_id =sl.id
                                left join
                                    stock_location sld on sm.location_dest_id = sld.id
                                where
                                    sm.state = 'done' and sm.date <= %s and sm.company_id = %s and sl.usage <> sld.usage and sm.product_id = %s and sm.id != %s
                                order by 
                                    sm.date desc, sm.id desc''',(inv.date,sm.company_id.id,product.id, sm.id))
                            data_crs = cr.dictfetchall()
                            ls_cost = 0.00
                            ls_pr = 0.00
                            if data_crs:
                                for data_cr in data_crs:
                                    sm_move_id = data_cr['sm_id']
                                    sm_date = data_cr['sm_date']
                                    if sm_date == inv.date:
                                        if sm_move_id > todo_move.id:
                                            continue
                                    if data_found:
                                        continue
                                    data_found = True
                                    ls_cost = (data_cr and 'last_cost' in data_cr and data_cr['last_cost']) or 0.00
                                    ls_pr = (data_cr and  'last_product' in data_cr and data_cr['last_product']) or 0.00
                            price_unit_avail[product.id] = ls_cost
                            product_avail[product.id] = ls_pr
                        if qty > 0:
                            product_avail[product.id] -= qty
                            if product_avail[product.id] < 0:
                                price_unit_avail[product.id] = 0.00
                            stock_move_obj.write(cr, uid, [sm.id],
                                    {'price_unit': price_unit_avail[product.id],
                                     'last_product_qty': product_avail[product.id],
                                     'last_cost_price': price_unit_avail[product.id]
                                     })
    
                            last_cost = stock_move_obj._update_the_less_move(cr, uid, product, price_unit_avail[product.id], product_avail[product.id], inv.date, sm.company_id.id, move_currency_id, sm)

                            product_obj.write(cr, uid, [product.id], {'standard_price': last_cost})
            self.write(cr, uid, [inv.id], {'state': 'done'}, context=context)
            self.post_inventory(cr, uid, inv, context=context)
        return True

    def post_inventory(self, cr, uid, inv, context=None):
        #The inventory is posted as a single step which means quants cannot be moved from an internal location to another using an inventory
        #as they will be moved to inventory loss, and other quants will be created to the encoded quant location. This is a normal behavior
        #as quants cannot be reuse from inventory location (users can still manually move the products before/after the inventory if they want).
        move_obj = self.pool.get('stock.move')
        if context is None:
            context = {}
        ctx = context.copy()

        ctx.update({'date': inv.date})


        move_obj.action_done(cr, uid, [x.id for x in inv.move_ids], context=ctx)

    def action_check(self, cr, uid, ids, context=None):
        """ Checks the inventory and computes the stock move to do
        @return: True
        """
        inventory_line_obj = self.pool.get('stock.inventory.line')
        stock_move_obj = self.pool.get('stock.move')
        if context is None:
            context = {}
        for inventory in self.browse(cr, uid, ids, context=context):
            #first remove the existing stock moves linked to this inventory
            move_ids = [move.id for move in inventory.move_ids]
            type = inventory.int_type_id.type
            context['inv_type'] = type
            stock_move_obj.unlink(cr, uid, move_ids, context=context)
            for line in inventory.line_ids:
                #compare the checked quantities on inventory lines to the theorical one
                if type == 'addiction':
                    if line.price_unit <= 0 and inventory.approved_zero == False:
                        raise osv.except_osv(_('Error !'), _('cannot process, found zero price unit for the product, can bypass it with permission.'))

                inventory_line_obj._resolve_inventory_line(cr, uid, line, context=context)

    def action_approve_zero(self, cr, uid, ids, context=None):
        self.write(cr, uid, ids, {'approved_zero': True, 'zero_approver': uid, 'zero_date':time.strftime('%Y-%m-%d %H:%M:%S')})
        return True

    def action_undo_zero(self, cr, uid, ids, context=None):
        self.write(cr, uid, ids, {'approved_zero': False, 'zero_approver': False, 'zero_date':False})
        return True
class stock_inventory_line(osv.osv):

    _inherit = "stock.inventory.line"

    _columns = {
        'note': fields.text('Note'),
        'price_unit': fields.float('Unit Price', digits_compute= dp.get_precision('Product Price'), help="Input Unit Price Only When do the adding qty"),
    }

    def _resolve_inventory_line(self, cr, uid, inventory_line, context=None):
        stock_move_obj = self.pool.get('stock.move')
        uom_obj =  self.pool.get('product.uom')
        type = ('inv_type' in context and context['inv_type']) or False
        diff = inventory_line.product_qty
        if not type:
            return
        #each theorical_lines where difference between theoretical and checked quantities is not 0 is a line for which we need to create a stock move
        vals = {
            'name': _('INV:') + (inventory_line.product_id.name or ''),
            'product_id': inventory_line.product_id.id,
            'product_uom': inventory_line.product_uom_id.id,
            'date': inventory_line.inventory_id.date,
            'company_id': inventory_line.inventory_id.company_id.id,
            'inventory_id': inventory_line.inventory_id.id,
            'state': 'confirmed',
            'restrict_lot_id': inventory_line.prod_lot_id.id,
            'restrict_partner_id': inventory_line.partner_id.id,
         }
        inventory_location_id = inventory_line.product_id.property_stock_inventory.id
        qty = uom_obj._compute_qty(cr, uid, inventory_line.product_uom_id.id, diff, inventory_line.product_id.uom_id.id)

        new_price = uom_obj._compute_price(cr, uid, inventory_line.product_uom_id.id, inventory_line.price_unit,
                            inventory_line.product_id.uom_id.id)

        if type == 'addiction':
            #found more than expected
            vals['location_id'] = inventory_location_id
            vals['location_dest_id'] = inventory_line.location_id.id
            vals['product_uom_qty'] = diff
            vals['price_unit'] = new_price
        else:
            vals['location_id'] = inventory_line.location_id.id
            vals['location_dest_id'] = inventory_location_id
            vals['product_uom_qty'] = diff
            #found less than expected

        return stock_move_obj.create(cr, uid, vals, context=context)

#Remove Later

class product_product(osv.osv):
    _inherit = "product.product"

    def _product_available(self, cr, uid, ids, field_names=None, arg=False, context=None):
        context = context or {}
        field_names = field_names or []

        domain_products = [('product_id', 'in', ids)]
        domain_quant, domain_move_in, domain_move_out = self._get_domain_locations(cr, uid, ids, context=context)
        domain_move_in += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel'))] + domain_products
        domain_move_out += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel'))] + domain_products
        domain_quant += domain_products
        if context.get('lot_id') or context.get('owner_id') or context.get('package_id'):
            if context.get('lot_id'):
                domain_quant.append(('lot_id', '=', context['lot_id']))
            if context.get('owner_id'):
                domain_quant.append(('owner_id', '=', context['owner_id']))
            if context.get('package_id'):
                domain_quant.append(('package_id', '=', context['package_id']))
            moves_in = []
            moves_out = []
        else:
            moves_in = self.pool.get('stock.move').read_group(cr, uid, domain_move_in, ['product_id', 'product_qty'], ['product_id'], context=context)
            moves_out = self.pool.get('stock.move').read_group(cr, uid, domain_move_out, ['product_id', 'product_qty'], ['product_id'], context=context)

        quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context)
#         print quants
        quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants))
#         print quants
        moves_in = dict(map(lambda x: (x['product_id'][0], x['product_qty']), moves_in))
        moves_out = dict(map(lambda x: (x['product_id'][0], x['product_qty']), moves_out))
        res = {}
        for id in ids:
            res[id] = {
                 'qty_available': quants.get(id, 0.0),
#                 'qty_available': 1234.0,
                'incoming_qty': moves_in.get(id, 0.0),
                'outgoing_qty': moves_out.get(id, 0.0),
                'virtual_available': quants.get(id, 0.0) + moves_in.get(id, 0.0) - moves_out.get(id, 0.0),
            }

        return res

    def _search_product_quantity(self, cr, uid, obj, name, domain, context):
        res = []
        for field, operator, value in domain:
            #to prevent sql injections
            assert field in ('qty_available', 'virtual_available', 'incoming_qty', 'outgoing_qty'), 'Invalid domain left operand'
            assert operator in ('<', '>', '=', '!=', '<=', '>='), 'Invalid domain operator'
            assert isinstance(value, (float, int)), 'Invalid domain right operand'

            if operator == '=':
                operator = '=='

            product_ids = self.search(cr, uid, [], context=context)
            ids = []
            if product_ids:
                #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget
                #the context that can be set with a location, an owner...
                for element in self.browse(cr, uid, product_ids, context=context):
                    if eval(str(element[field]) + operator + str(value)):
                        ids.append(element.id)
            res.append(('id', 'in', ids))
        return res

    _columns = {
        'qty_available': fields.function(_product_available, multi='qty_available',
            type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
            string='Quantity On Hand',
            fnct_search=_search_product_quantity,
            help="Current quantity of products.\n"
                 "In a context with a single Stock Location, this includes "
                 "goods stored at this Location, or any of its children.\n"
                 "In a context with a single Warehouse, this includes "
                 "goods stored in the Stock Location of this Warehouse, or any "
                 "of its children.\n"
                 "stored in the Stock Location of the Warehouse of this Shop, "
                 "or any of its children.\n"
                 "Otherwise, this includes goods stored in any Stock Location "
                 "with 'internal' type."),
    }

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
